﻿using System;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Threading.Tasks;
using AnthStat.Infrastructure;
using AnthStat.Domain.Collections;

namespace AnthStat.Domain
{
    /// <summary>
    /// Class representation of an AnthStat project file
    /// </summary>
    public sealed class Project
    {
        #region Members
        private XmlPersonRepository _repository;
        private readonly DataCaptureMode _dataCaptureMode = DataCaptureMode.IndividualAssessments;
        #endregion // Members

        #region Properties
        public string Name { get; set; }
        public string Author { get; set; }
        public string IndicatorSet { get; set; }
        public string Description { get; set; }
        public string LocationOnDisk { get; set; }

        /// <summary>
        /// The collection of visits for this child
        /// </summary>
        public PersonCollection People { get; private set; }
        #endregion // Properties

        #region Constructors
        /// <summary>
        /// Default constructor
        /// </summary>
        /// <param name="name">The name of the project</param>
        /// <param name="description">The project's description</param>
        /// <param name="fileName">The file name of the project</param>
        /// <param name="dataCaptureMode">The type of data capture mode for this project</param>
        public Project(string name, string description, string fileName, DataCaptureMode dataCaptureMode)
        {
            // pre
            Contract.Requires(!String.IsNullOrEmpty(name));
            Contract.Requires(!String.IsNullOrEmpty(fileName));

            // post
            Contract.Ensures(!String.IsNullOrEmpty(Name));
            Contract.Ensures(People != null);

            Name = name;
            Description = description;
            People = new PersonCollection();
            _dataCaptureMode = dataCaptureMode;

            SetFileLocation(fileName);
        }

        /// <summary>
        /// Default constructor
        /// </summary>
        /// <param name="name">The name of the project</param>
        /// <param name="description">The project's description</param>
        /// <param name="dataCaptureMode">The type of data capture mode for this project</param>
        public Project(string name, string description, DataCaptureMode dataCaptureMode)
        {
            // pre
            Contract.Requires(!String.IsNullOrEmpty(name));

            // post
            Contract.Ensures(!String.IsNullOrEmpty(Name));
            Contract.Ensures(People != null);

            Name = name;
            Description = description;
            People = new PersonCollection();
            _dataCaptureMode = dataCaptureMode;
        }
        #endregion // Constructors

        #region Methods

        internal IndicatorSet GetIndicatorSet()
        {
            if (IndicatorSet.Equals("WHO2006", StringComparison.OrdinalIgnoreCase))
            {
                return AnthStat.Domain.IndicatorSet.WHO2006;
            }
            else if (IndicatorSet.Equals("WHO2007", StringComparison.OrdinalIgnoreCase))
            {
                return AnthStat.Domain.IndicatorSet.WHO2007;
            }
            else
            {
                return AnthStat.Domain.IndicatorSet.WHO20062007;
            }
        }

        /// <summary>
        /// Sets the location of the project file
        /// </summary>
        /// <param name="fileName">The file name of the project</param>
        public void SetFileLocation(string fileName)
        {
            // pre
            Contract.Requires(!String.IsNullOrEmpty(fileName));
            Contract.Requires(String.IsNullOrEmpty(LocationOnDisk));

            LocationOnDisk = fileName;

            PopulateCollections();
        }

        private void PopulateCollections()
        {
            // pre
            Contract.Requires(_repository == null);

            // post
            Contract.Ensures(_repository != null);

            _repository = new XmlPersonRepository(LocationOnDisk, "Person"); // Dependency Injection ??

            foreach (var p in _repository.GetAll())
            {
                p.AttachProject(this);
                People.Add(p);

                p.MeasurementAdded += MeasurementAdded;
                p.MeasurementRemoved += MeasurementRemoved;
            }
        }

        private void MeasurementRemoved(object sender, Events.MeasurementEventArgs e)
        {
            //throw new NotImplementedException();
        }

        private void MeasurementAdded(object sender, Events.MeasurementEventArgs e)
        {
            //throw new NotImplementedException();
        }

        /// <summary>
        /// Adds a person to the project
        /// </summary>
        /// <param name="person">The person to add to the project</param>
        public void AddPerson(Person person)
        {
            // pre
            Contract.Requires(!People.Contains(person));

            // post
            Contract.Ensures(People.Contains(person));

            People.Add(person);

            _repository.Insert(person, false);

            person.MeasurementAdded += MeasurementAdded;
            person.MeasurementRemoved += MeasurementRemoved;
        }

        /// <summary>
        /// Removes a person from the project
        /// </summary>
        /// <param name="person">The person to remove from the project</param>
        public void RemovePerson(Person person)
        {
            // pre
            Contract.Requires(People.Contains(person));

            // post
            Contract.Ensures(!People.Contains(person));

            person.MeasurementAdded -= MeasurementAdded;
            person.MeasurementRemoved -= MeasurementRemoved;

            People.Remove(person);

            _repository.Delete(person, false);
        }

        /// <summary>
        /// Removes a person from the project
        /// </summary>
        /// <param name="globalRecordId">The Id of the person who should be removed from the project</param>
        public void RemovePerson(Guid globalRecordId)
        {
            // pre
            Contract.Requires(globalRecordId != null);

            string guid = globalRecordId.ToString();

            foreach (var person in People.Where(person => person.GlobalRecordId.Equals(guid, StringComparison.OrdinalIgnoreCase)))
            {
                RemovePerson(person);
                break;
            }
        }

        /// <summary>
        /// Saves the project to disk
        /// </summary>
        public string Save()
        {
            foreach (Person person in People)
            {
                _repository.Update(person, false);
            }

            return _repository.Save(); // ugly but required to do the file save in VM due to incompatible PCL file save mechanisms across desktop and WinRT
        }

        #endregion // Methods
    }
}
